using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.Windows.Forms;

using Conscript;
using Conscript.Compiler;
using Conscript.Runtime;

namespace DungeonOfDespair
{
    public class Dungeon
    {
        private ScriptManager m_scriptManager;
        private Script m_scriptCutscenes;
        private Player m_player;
        private Boss m_boss;
        private bool m_bCheat;
        private Dictionary<String, Room> m_dictRooms;
        private Room m_roomCurrent;
        private Room m_roomPortal;
        private Room m_roomVialRed;
        private Room m_roomVialGreen;
        private Room m_roomVialYellow;
        private Room m_roomVialBlue;

        private void ValidateDoorLink(String strRoomID, String strDoor, String strRoomLink)
        {
            if (strRoomLink == null) return;
            if (m_dictRooms.ContainsKey(strRoomLink)) return;

            throw new Exception(strDoor + " door in room '"
                + strRoomID + "' does not link to a valid room.");
        }

        public Dungeon(ScriptManager scriptManager, Player player, Boss boss)
        {
            m_scriptManager = scriptManager;
            m_scriptCutscenes = null;
            m_player = player;
            m_boss = boss;
            m_bCheat = false;
            m_dictRooms = new Dictionary<string, Room>();
            m_roomCurrent = null;
            m_roomPortal = null;
            m_roomVialRed = null;
            m_roomVialGreen = null;
            m_roomVialYellow = null;
            m_roomVialBlue = null;
        }

        public void Load()
        {
            // load cutscenes script
            m_scriptCutscenes
                = new Script(m_scriptManager, "Cutscenes.cns");

            // load dungeon script
            Script script = new Script(m_scriptManager, "Dungeon.cns");

            // create script context
            ScriptContext scriptContext = new ScriptContext(script);

            // execute script's main function for dungeon data
            while (!scriptContext.Terminated)
                scriptContext.Execute();

            // get main dungeon structure
            AssociativeArray assocativeArrayDungeon
                = (AssociativeArray) scriptContext.LocalDictionary["s_dungeon"];

            // get "Cheat" member
            if (assocativeArrayDungeon.ContainsKey("Cheat"))
                m_bCheat = (bool)assocativeArrayDungeon["Cheat"];

            // get "Rooms" member
            AssociativeArray associativeArrayRooms
                = (AssociativeArray)assocativeArrayDungeon["Rooms"];

            // process all rooms
            foreach (String strRoomId in associativeArrayRooms.Keys)
            {
                Room room = new Room(this, strRoomId);

                // get current room structure
                AssociativeArray associativeArrayRoom 
                    = (AssociativeArray)associativeArrayRooms[strRoomId];

                // load from script
                room.Load(associativeArrayRoom);

                // store room structure
                m_dictRooms[strRoomId] = room;
            }

            // determine all doorways valid
            foreach (Room room in m_dictRooms.Values)
            {
                Doors doors = room.Doors;

                ValidateDoorLink(room.Id, "North", doors.North);
                ValidateDoorLink(room.Id, "South", doors.South);
                ValidateDoorLink(room.Id, "Wes", doors.West);
                ValidateDoorLink(room.Id, "East", doors.East);
            }

            // determine starting room
            if (!assocativeArrayDungeon.ContainsKey("StartingRoom"))
                throw new Exception("Dungeon StartingRoom not defined.");
            m_roomCurrent =
                m_dictRooms[(String) assocativeArrayDungeon["StartingRoom"]];

            // determine portal room
            if (!assocativeArrayDungeon.ContainsKey("PortalRoom"))
                throw new Exception("Dungeon PortalRoom not defined.");
            m_roomPortal = m_dictRooms[(String)assocativeArrayDungeon["PortalRoom"]];

            // determine red vial room
            if (!assocativeArrayDungeon.ContainsKey("RedVialRoom"))
                throw new Exception("Dungeon RedVialRoom not defined.");
            m_roomVialRed = m_dictRooms[(String)assocativeArrayDungeon["RedVialRoom"]];

            // determine green vial room
            if (!assocativeArrayDungeon.ContainsKey("GreenVialRoom"))
                throw new Exception("Dungeon GreenVialRoom not defined.");
            m_roomVialGreen = m_dictRooms[(String)assocativeArrayDungeon["GreenVialRoom"]];

            // determine yellow vial room
            if (!assocativeArrayDungeon.ContainsKey("YellowVialRoom"))
                throw new Exception("Dungeon YellowVialRoom not defined.");
            m_roomVialYellow = m_dictRooms[(String)assocativeArrayDungeon["YellowVialRoom"]];

            // determine blue vial room
            if (!assocativeArrayDungeon.ContainsKey("BlueVialRoom"))
                throw new Exception("Dungeon BlueVialRoom not defined.");
            m_roomVialBlue = m_dictRooms[(String)assocativeArrayDungeon["BlueVialRoom"]];

            // reset current room
            m_roomCurrent.Reset();
        }

        private void GoNorth(Player player)
        {
            if (m_roomCurrent.Doors.North == null) return;
            m_roomCurrent = m_dictRooms[m_roomCurrent.Doors.North];
            m_roomCurrent.ToggleRoomImage();
            m_roomCurrent.Reset();
            player.Y = 368;
        }

        private void GoSouth(Player player)
        {
            if (m_roomCurrent.Doors.South == null) return;
            m_roomCurrent = m_dictRooms[m_roomCurrent.Doors.South];
            m_roomCurrent.ToggleRoomImage();
            m_roomCurrent.Reset();
            player.Y = 108;
        }

        private void GoWest(Player player)
        {
            if (m_roomCurrent.Doors.West == null) return;
            m_roomCurrent = m_dictRooms[m_roomCurrent.Doors.West];
            m_roomCurrent.ToggleRoomImage();
            m_roomCurrent.Reset();
            player.X = 534;
        }

        private void GoEast(Player player)
        {
            if (m_roomCurrent.Doors.East == null) return;
            m_roomCurrent = m_dictRooms[m_roomCurrent.Doors.East];
            m_roomCurrent.ToggleRoomImage();
            m_roomCurrent.Reset();
            player.X = 106;
        }

        public void Update(Player player)
        {
            if (m_roomCurrent == null) return;

            if (m_roomCurrent.ExitingNorth(player))
                GoNorth(player);
            if (m_roomCurrent.ExitingSouth(player))
                GoSouth(player);
            if (m_roomCurrent.ExitingWest(player))
                GoWest(player);
            if (m_roomCurrent.ExitingEast(player))
                GoEast(player);

            m_roomCurrent.Update();
        }

        public void Draw(Graphics graphics)
        {
            if (m_roomCurrent == null) return;

            m_roomCurrent.Draw(graphics);
        }

        public ScriptManager ScriptManager
        {
            get { return m_scriptManager; }
        }

        public Script CutsceneScript
        {
            get { return m_scriptCutscenes; }
        }

        public Player Player
        {
            get { return m_player; }
        }

        public Boss Boss
        {
            get { return m_boss; }
        }

        public bool Cheat
        {
            get { return m_bCheat; }
        }

        public Room CurrentRoom
        {
            get { return m_roomCurrent; }
        }

        public Room PortalRoom
        {
            get { return m_roomPortal; }
        }

        public Room RedVialRoom
        {
            get { return m_roomVialRed; }
            set { m_roomVialRed = value; }
        }

        public Room GreenVialRoom
        {
            get { return m_roomVialGreen; }
            set { m_roomVialGreen = value; }
        }

        public Room YellowVialRoom
        {
            get { return m_roomVialYellow; }
            set { m_roomVialYellow = value; }
        }

        public Room BlueVialRoom
        {
            get { return m_roomVialBlue; }
            set { m_roomVialBlue = value; }
        }

        public bool PortalActive
        {
            get
            {
                return m_roomVialRed == m_roomPortal
                    && m_roomVialGreen == m_roomPortal
                    && m_roomVialYellow == m_roomPortal
                    && m_roomVialBlue == m_roomPortal;
            }
        }
    }
}
